﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls.Base;
using Silvermoon.Controls.Classes;
using System.Drawing;
using Silvermoon.Images;
using Silvermoon.Shapes;
using Silvermoon.OpenGL;
using Silvermoon.Core;
using Silvermoon.Controls.Toolbars;
using Silvermoon.UI;
using Silvermoon.Animations;
using Silvermoon.Windows.Styles;
using Silvermoon.Windows;

namespace Silvermoon.Controls
{
    /// <summary>
    /// Toolbar implementation which looks like windows phone 7's ApplicationBar.
    /// </summary>
    public class Toolbar : ToolbarBase
    {
        #region const

        const int ButtonSpace = 24;
        public const int DefaultButtonSize = 64;

        #endregion
        #region accessors

        public static PropertyAccessor<Toolbar, float> MenuExpansionProperty = PropertyAccessor.Register<Toolbar, float>(
            "MenuExpansion",
            c => c.MenuExpansion,
            (c, v) => c.MenuExpansion = v);

        #endregion
        #region fields

        private bool isPanning;
        private float expansion = 0f;
        private ToolbarMenuButton toolbarMenubutton;
        private bool isMenuButtonVisible;
        private ButtonCollection buttons;
        private MenuListView menuListView;
        private ListViewGroup group;
        private ItemCollection menuItems;

        #endregion
        #region ctors/dtors


        static Toolbar()
        {
            VisualStateManager.RegisterClassManager(typeof(Toolbar), ToolbarStateManagers.ToolbarStateManager);
        }

        public Toolbar()
            : base()
        {
            menuItems = new ItemCollection();
            menuItems.ListChanged += new EventHandler<ItemChangedEventArgs>(OnMenuItemsChanged);
            buttons = new ButtonCollection(this);
            buttons.ListChanged += new EventHandler(OnButtonsChanged);
            Size = new Size(ToolbarSize, ToolbarSize);
            ControlFlags |= ControlFlags.CanPan | ControlFlags.HitTestVisible;

            Styles.SetClass(typeof(Button), MetroButtonStyle.Instance);
        }

        public Toolbar(params ButtonBase[] buttons)
            : this()
        {
            Buttons.AddRange(buttons);
        }


        protected override void Dispose(bool dispose)
        {
            if (toolbarMenubutton != null)
            {
                VisibleControls.Remove(toolbarMenubutton);
                toolbarMenubutton.Dispose();
                toolbarMenubutton = null;
            }
            DisposeObject(menuListView);
            DisposeObject(group);
            group = null;
            if (menuItems != null)
            {
                menuItems.Clear();
                menuItems = null;
            }
            if (buttons != null)
            {
                buttons.Clear();
                buttons = null;
            }
            base.Dispose(dispose);
        }

        #endregion
        #region props

        public int ItemsPerRow
        {
            get { return Group.ItemsPerRow; }
            set
            {
                if (Group.ItemsPerRow != value)
                {
                    Group.ItemsPerRow = value;
                }
            }
        }

        /// <summary>
        /// Gets whether the toolbar allows landscape mode.
        /// This is the case if no button is wider than the toolbar's width.
        /// </summary>
        public override bool EnableLandscape
        {
            get
            {
                return GetEnableLandscape();
            }
        }

        /// <summary>
        /// Gets or sets the transition factor between menu Collapsed and Expanded where 0f is collapsed and 1f is expanded. The range is between 0 and 1.
        /// This property is used for animation.
        /// </summary>
        public float MenuExpansion
        {
            get { return expansion; }
            set
            {
                if (expansion != value)
                {
                    expansion = value;
                    OnMenuExpansionChanged(value);
                }
            }
        }


        /// <summary>
        /// Gets the width or height (depending on screen's orientation) of the toolbar that is currently transitioned.
        /// See also <seealso cref="P:DesiredSize"/> property.
        /// </summary>
        /// <value></value>
        public override int CurrentSize
        {
            get
            {
                if (Orientation == Orientation.Portrait)
                {
                    int h = MenuItemsRect.Height;

                    return (int)(h * expansion) + CollapsedSize;
                }
                else
                {
                    int w = MenuItemsRect.Width;
                    return (int)(w * expansion) + CollapsedSize;
                }
            }
        }

        /// <summary>
        /// Gets the desired width or height (depending on the screen's orientation) that the toolbar is about to transition to.
        /// This value informs the window about the required size for ClientBounds.
        /// See also <seealso cref="P:CurrentSize"/> property.
        /// </summary>
        /// <value></value>
        public override int DesiredSize
        {
            get
            {
                if (Orientation == Orientation.Portrait)
                {
                    return CollapsedSize + (expansion == 1f ? MenuItemsRect.Height : 0);
                }
                else
                {
                    return CollapsedSize + (expansion == 1f ? MenuItemsRect.Width : 0);
                }
            }
        }

        /// <summary>
        /// Gets the collection of menu items.
        /// </summary>
        public IList<Item> MenuItems
        {
            get { return menuItems; }
        }

        private MenuListView MenuItemsView
        {
            get
            {
                if (menuListView == null)
                {
                    menuListView = new MenuListView();
                }
                return menuListView;
            }
        }

        protected ListViewGroup Group
        {
            get
            {
                if (group == null)
                {
                    group = new ListViewGroup();
                    MenuItemsView.ItemsSource = new Item[] { group };
                    //MenuItemsView.Items.Add(group);
                }
                return group;
            }
        }

        protected Rectangle MenuItemsRect
        {
            get
            {
                if (Orientation == Orientation.Portrait)
                {
                    int h = Math.Min(menuListView != null ? menuListView.VirtualHeight : 0, MaxMenuHeight);
                    return new Rectangle(0, CollapsedSize, Width, h);
                }
                else
                {
                    //int w = Height - CollapsedSize * 2; ;
                    int w = Height - CollapsedSize;
                    return new Rectangle(CollapsedSize, 0, w, Height);
                }
            }
        }

        protected Rectangle ToolbarMenuButtonRect
        {
            get
            {
                if (isMenuButtonVisible == false) return Rectangle.Empty;
                if (Orientation == Orientation.Portrait)
                {
                    int h = ToolbarMenuButton.Height;
                    int top = (CollapsedSize - h) / 2 + (int)(ButtonVisibility * CollapsedSize);
                    int w = ToolbarMenuButton.Width;
                    return new Rectangle(Width - w - 2, top, w, h);
                }
                else
                {
                    int w = ToolbarMenuButton.Width;
                    int left = (CollapsedSize - w) / 2 + (int)(ButtonVisibility * CollapsedSize);
                    int h = ToolbarMenuButton.Height;
                    return new Rectangle(left, 2, w, h);
                }
            }
        }

        protected ToolbarMenuButton ToolbarMenuButton
        {
            get
            {
                if (toolbarMenubutton == null)
                {
                    toolbarMenubutton = new ToolbarMenuButton();
                }
                return toolbarMenubutton;
            }
        }

        /// <summary>
        /// Gets the collection of buttons for the toolbar.
        /// </summary>
        public ButtonCollection Buttons { get { return buttons; } }

        /// <summary>
        /// Gets or sets whether the menu button is visible.
        /// </summary>
        public bool IsMenuButtonVisible
        {
            get { return isMenuButtonVisible; }
            set
            {
                if (isMenuButtonVisible != value)
                {
                    isMenuButtonVisible = value;
                    OnMenuButtonVisibleChanged();
                }
            }
        }

        protected virtual void OnMenuExpansionChanged(float value)
        {
            Invalidate(InvalidationFlag.Size);
            if (expansion == 0f) OnMenuHidden();
            else if (expansion == 1f) OnMenuShown();
            if (expansion > 0f && menuItems.Count > 0) Group.ItemsSource = menuItems; else if (menuListView != null) Group.ItemsSource = null;
            if (Window != null)
            {

                Window.Invalidate(InvalidationFlag.Layout);
                if (value == 1f) Window.UpdateClientBounds();
            }
        }

        protected virtual void OnMenuShowing()
        {
            Group.ItemsSource = menuItems;
        }

        protected virtual void OnMenuHiding()
        {
        }

        protected virtual void OnMenuShown()
        {
        }

        protected void OnMenuHidden()
        {
            if (group != null) group.ItemsSource = null;
        }


        #endregion
        #region methods

        private void OnMenuItemsChanged(object sender, ItemChangedEventArgs e)
        {
            IsMenuButtonVisible = menuItems.Count > 0;
        }


        protected override void ApplyStyle()
        {
            base.ApplyStyle();
            if (TextColor.IsEmpty) TextColor = Color.White;

        }

        /// <summary>
        /// Gets whether the toolbar supports landscape orientation.
        /// This is the case if no button is wider than the toolbar's width in landscape mode.
        /// </summary>
        /// <returns>True if landscape is supported, otherwise false.</returns>
        protected virtual bool GetEnableLandscape()
        {
            int min = CollapsedSize;
            if (buttons != null)
            {
                foreach (ButtonBase b in buttons)
                {
                    if (b.Width > min) return false;
                }
            }
            return true;
        }

        protected internal override void OnBeginPan(PanEventArgs e)
        {
            if (!e.Handled)
            {
                if ((Orientation == Orientation.Portrait && e.VerticalMotion) || (Orientation == Orientation.Landscape && e.HorizontalMotion))
                {
                    isPanning = true;
                    e.Handled = true;
                    int x = -(int)(MenuItemsRect.Width * MenuExpansion);
                    int y = (int)(MenuItemsRect.Height * MenuExpansion);
                    e.Value = new Point(x, y);
                }
            }
            base.OnBeginPan(e);
        }

        protected internal override void OnPan(PanEventArgs e)
        {
            float value = Orientation == Orientation.Portrait ? (float)e.Value.Y / (float)MenuItemsRect.Height : (float)(-e.Value.X) / (float)MenuItemsRect.Width;
            if (value < 0f) value = 0f; else if (value > 1f) value = 1f;
            MenuExpansion = value;
            e.Handled = true;

            base.OnPan(e);
        }

        /// <summary>
        /// Sets all properties to a defined value.
        /// If overridden, this method determines the ideal values of specific properties, for instance, when a property is changed as the
        /// uses swipes between possible values.
        /// It's best practice to access each property value  using PropertyAccessor.GetUnanimatedValue.
        /// </summary>
        /// <param name="animated">True when to allowed to tranistion to a new value, otherwise false.</param>
        public override void EnsureRestState(bool animated)
        {
            if (IsToolbarIntermediate())
            {
                float tranistion = MenuExpansion;
                bool isExpanded = tranistion > 0.5f;

                string state = isExpanded ? "Expanded" : "Collapsed";
                VisualStateManager.GoToState(this, state, animated);
                IsExpanded = isExpanded;
            }
        }

        private bool IsToolbarIntermediate()
        {
            return (MenuExpansion != 0f && MenuExpansion != 1f);
        }

        protected override void EnsureRestStateOnSizeChanged()
        {
        }

        protected internal override void OnEndPan(EventArgs e)
        {
            if (isPanning)
            {
                isPanning = false;
                if (!MenuExpansionProperty.IsAnimated(this)) EnsureRestState(true);
                base.OnEndPan(e);
            }
        }

        protected internal override void OnTouchUp(TouchEventArgs e)
        {
            base.OnTouchUp(e);
            EnsureRestState(true);
        }

        protected internal override void OnFlick(TouchEventArgs e)
        {
            if (menuListView != null)
            {
                if (Orientation == Orientation.Portrait)
                {
                    switch (e.Direction)
                    {
                        case PanDirection.Up:
                            e.Handled = true;
                            IsExpanded = true;
                            break;

                        case PanDirection.Down:
                            e.Handled = true;
                            IsExpanded = false;
                            break;
                    }
                }
                else
                {
                    switch (e.Direction)
                    {
                        case PanDirection.Left:
                            e.Handled = true;
                            IsExpanded = true;
                            break;

                        case PanDirection.Right:
                            e.Handled = true;
                            IsExpanded = false;
                            break;
                    }
                }
            }
        }

        protected override void OnExpansionChanged()
        {
            if (IsExpanded) OnMenuShowing(); else OnMenuHiding();
            if (IsExpanded == true && menuListView != null) menuListView.YOffset = 0;
            base.OnExpansionChanged();
        }

        protected virtual void OnMenuButtonVisibleChanged()
        {
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        protected override void OnLayout()
        {
            base.OnLayout();
            if (CheckFlag(InvalidationFlag.Size | InvalidationFlag.Layout))
            {
                VisibleControls.ClearQuick();
                if (Orientation == Silvermoon.Windows.Orientation.Portrait)
                {
                    LayoutPortrait();
                }
                else
                {
                    LayoutLandscape();
                }
                if (MenuExpansion > 0f)
                {
                    VisibleControls.AddLast(MenuItemsView);
                    MenuItemsView.Bounds = MenuItemsRect;
                }
            }
        }

        private void LayoutLandscape()
        {
            int height = Height;
            Width = CollapsedSize + (isMenuButtonVisible ? MenuItemsRect.Width : 0);
            int totalButtonHeight = CalculateTotalButtonWidth();

            int top = (Height + totalButtonHeight) / 2;

            foreach (ButtonBase button in buttons.GetVisibleButtons())
            {
                Rectangle bounds = CalculateLandscapeButtonBounds(top, button);
                button.Bounds = bounds;
                top -= ButtonSpace + bounds.Height;
                VisibleControls.AddLast(button);
            }
            if (isMenuButtonVisible)
            {
                VisibleControls.AddLast(ToolbarMenuButton);
                ToolbarMenuButton.Bounds = ToolbarMenuButtonRect;
            }
        }

        private void LayoutPortrait()
        {
            int width = Width;
            Height = CollapsedSize + (isMenuButtonVisible ? MenuItemsRect.Height : 0);
            int totalButtonWidth = CalculateTotalButtonWidth();

            int left = (Width - totalButtonWidth) / 2;

            foreach (ButtonBase button in buttons.GetVisibleButtons())
            {
                button.Bounds = CalculatePortraitButtonBounds(left, button);
                left += button.Width + ButtonSpace;
                VisibleControls.AddLast(button);
            }
            if (isMenuButtonVisible)
            {
                ToolbarMenuButton.Bounds = ToolbarMenuButtonRect;
                VisibleControls.AddLast(ToolbarMenuButton);
            }
        }

        private Rectangle CalculatePortraitButtonBounds(int left, ButtonBase button)
        {
            Size size = GetButtonSize(button);
            int top = (CollapsedSize - size.Height) / 2 + (int)(ButtonVisibility * CollapsedSize);
            int w = button.Width;
            if (w == 0) w = DefaultButtonSize;
            return new Rectangle(left, top, size.Width, size.Height);
        }

        private Rectangle CalculateLandscapeButtonBounds(int top, ButtonBase button)
        {
            Size size = GetButtonSize(button);
            int left = (CollapsedSize - size.Width) / 2 + (int)(ButtonVisibility * CollapsedSize);
            return new Rectangle(left, top - size.Height, size.Width, size.Height);
        }

        private Size GetButtonSize(ButtonBase button)
        {
            int w = Math.Max(DefaultButtonSize, button.Width);
            int h = Math.Max(DefaultButtonSize, button.Height);
            return new Size(w, h);
        }

        private int CalculateTotalButtonWidth()
        {
            int total = 0;
            bool portrait = Orientation == Orientation.Portrait;
            foreach (ButtonBase button in buttons.GetVisibleButtons())
            {
                Size size = GetButtonSize(button);
                total += portrait ? size.Width : size.Height;
            }
            int count = buttons.GetVisibleButtons().Count;
            if (count > 1) total += ButtonSpace * (count - 1);
            return total;
        }

        private void OnButtonsChanged(object sender, EventArgs e)
        {
            Invalidate(InvalidationFlag.Layout);
        }

        public ContentButton CreateButton()
        {
            ContentButton button = new ToolbarButton();
            buttons.Add(button);
            return button;
        }

        //protected override Shape CreateDefaultBackground()
        //{
        //    //return Color.Black;
        //    return new BackgroundShape { Color = SysColors.HeaderColor };
        //}

        protected override void OnTextColorChanged()
        {
            base.OnTextColorChanged();
            Color color = this.Color;
            foreach (ButtonBase button in buttons) button.Color = color;
        }

        protected override void OnButtonVisibilityChanged()
        {
            base.OnButtonVisibilityChanged();
            Invalidate(InvalidationFlag.Layout);
        }

        protected internal override void OnTap(TapEventArgs e)
        {
            if (e.Handled) IsExpanded = false;
            base.OnTap(e);
        }

        internal void InvalidateButtons()
        {
            Invalidate(InvalidationFlag.Layout);
            HideButtons(true);
            //SetState(ToolbarStateManagers.ButtonsHidden);
        }


        protected override void OnInitialized()
        {
            buttons.Validate();
            base.OnInitialized();
        }

        protected override void OnButtonsHidden()
        {
            base.OnButtonsHidden();
            if (buttons.IsInvalidated)
            {
                buttons.Validate();
                ShowButtons(true);
            }
        }

        public MenuItem AddMenuItem(string text)
        {
            MenuItem item = new MenuItem(text);
            MenuItems.Add(item);
            return item;
        }

        public MenuItem AddMenuItem(string text, ICommand command)
        {
            MenuItem item = new MenuItem(text, command);
            MenuItems.Add(item);
            return item;
        }

        public ToolbarButton AddButton(Shape content)
        {
            ToolbarButton button = new ToolbarButton(content);
            Buttons.Add(button);
            return button;
        }

        public ToolbarButton AddButton(Shape content, ICommand command)
        {
            ToolbarButton button = new ToolbarButton(content, command);
            Buttons.Add(button);
            return button;
        }


        #endregion


    }
}
